package com.zmj.sy.mom.srv.aps.service;

import cn.hutool.core.date.DatePattern;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.zmj.sy.mom.srv.aps.bean.bo.ApsWeekDay;
import com.zmj.sy.mom.srv.aps.bean.entity.base.BaseEntity;
import com.zmj.sy.mom.srv.aps.bean.entity.order.*;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.*;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseSelectItemResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseSelectResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.prescheduled.*;
import com.zmj.sy.mom.srv.aps.bean.vo.reverseorder.ReverseOrderLoadsDayResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.reverseorder.ReverseOrderLoadsResVo;
import com.zmj.sy.mom.srv.aps.config.ProjectConfig;
import com.zmj.sy.mom.srv.aps.mapper.*;
import com.zmj.sy.mom.srv.aps.utils.*;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RequiredArgsConstructor
@Service
public class PreScheduledService {


    private final OrderMapper orderMapper;
    private final ApsWorkDetailMapper apsWorkDetailMapper;
    private final PhaseMapper phaseMapper;
    private final GroupMapper groupMapper;

    private final ProjectConfig projectConfig;
    private final OrderBomMapper orderBomMapper;
    private final ApsWorkOrderMapper apsWorkOrderMapper;
    private final SinoCamReceiveNestPlateMapper sinoCamReceiveNestPlateMapper;
    private final SinoCamReceiveNestNestMapper sinoCamReceiveNestNestMapper;
    private final ApsDetailWeekdayMapper apsDetailWeekdayMapper;
    private final ApsOrderMapper apsOrderMapper;
    private final StationWeekdaySchemeMapper stationWeekdaySchemeMapper;
    private final ShiftMapper shiftMapper;
    private final StationWeekdayRuleMapper stationWeekdayRuleMapper;
    private final BreakPeriodMapper breakPeriodMapper;
    private final ReverseTopMapper reverseTopMapper;
    private final ObjectMapper jacksonObjectMapper;
    private final ReverseOrderMapper reverseOrderMapper;
    private final StationMapper stationMapper;
    private final WeekdayRuleMapper weekdayRuleMapper;


    public BaseSelectResVo finishSelect(PreScheduledFinishSelectReqVo reqVo) {
        LambdaQueryWrapper<Order> wrapper = Wrappers.lambdaQuery();
        wrapper.like(StringUtils.hasText(reqVo.getOrderCode()), Order::getConstructNo, reqVo.getOrderCode());
        wrapper.in(Order::getOrderType, 2, 5);// 2MBD
        wrapper.ge(Order::getOrderStatus,5);// 5已排产
        wrapper.orderByDesc(Order::getId);
        List<Order> orders = orderMapper.selectList(wrapper);

        List<BaseSelectItemResVo> collect = orders.stream()
                .filter(e-> e.getMaterCode().matches(".+" + WorkshopProp.PH_PATTERN + "$"))
                .map(order -> {
                    BaseSelectItemResVo resVo = new BaseSelectItemResVo();
                    resVo.setText(order.getConstructNo() + " - " + order.getMaterName());
                    resVo.setValue(order.getId());
                    return resVo;
                })
                .collect(Collectors.toList());

        return BaseSelectResVo.of(collect);
    }

    @SneakyThrows
    public PreScheduledAnalysisReportResVo analysisReport(PreScheduledAnalysisReportReqVo reqVo, ReverseOrderLoadsResVo reverseOrderLoadsResVo) {

        Order order = orderMapper.selectById(reqVo.getId());

        PreScheduledAnalysisReportResVo resVo = MkBeanUtils.copyProperties(order, PreScheduledAnalysisReportResVo.class);
        log.info("主要产品部件：{}；订单数量：{}；计划时间：{} 至 {}。", order.getMaterName(), order.getOrderQty(), order.getPlanStartDateTime(), order.getPlanEndDateTime());

        Integer count = apsOrderMapper.lambdaQuery().eq(ApsOrder::getOrderId, order.getId()).count();
        resVo.setPackageQty(count);
//        List<ApsWorkDetail> phaseAwdList = apsWorkDetailMapper.query()
//                .select("phase_code, sum(work_time) as work_time")
//                .eq("order_id", reqVo.getId())
//                .groupBy("phase_code")
//                .orderByDesc("work_time")
//                .list();
//
//        List<Phase> phaseList = phaseMapper.lambdaQuery().list();
//        Map<String, Phase> phaseMap = phaseList.stream().collect(Collectors.toMap(Phase::getMyCode, Function.identity()));
//
//        List<PreScheduledAnalysisReportPhaseResVo> phaseData = phaseAwdList.stream().map(e -> {
//                    PreScheduledAnalysisReportPhaseResVo phase = new PreScheduledAnalysisReportPhaseResVo();
//                    Phase phase1 = phaseMap.get(e.getPhaseCode());
//
//                    phase.setPhaseName(phase1.getName());
//                    phase.setPhaseCode(e.getPhaseCode());
//                    phase.setWorkHours(new BigDecimal(e.getWorkTime()).divide(new BigDecimal(60 * 60), 2, RoundingMode.UP));
//
//                    return phase;
//                })
//                .collect(Collectors.toList());
//        resVo.setPhaseData(phaseData);

        List<ApsWorkDetail> apsWorkDetailList = apsWorkDetailMapper.query()
                .select(" aps_order_id aps_order_id , min(plan_start_date_time) plan_start_date_time, max(plan_end_date_time) plan_end_date_time ")
                .eq("order_id", reqVo.getId())
                .groupBy("aps_order_id")
                .list();

        List<ApsOrder> apsOrderList = apsOrderMapper.lambdaQuery().in(ApsOrder::getId, apsWorkDetailList.stream().map(ApsWorkDetail::getApsOrderId).collect(Collectors.toList())).list();
        Map<Integer, ApsOrder> apsOrderMap = apsOrderList.stream().collect(Collectors.toMap(ApsOrder::getId, Function.identity()));
        List<PreScheduledAnalysisReportPhaseResVo> collect1 = apsWorkDetailList.stream().map(e -> {
            PreScheduledAnalysisReportPhaseResVo phase = new PreScheduledAnalysisReportPhaseResVo();
            ApsOrder apsOrder = apsOrderMap.get(e.getApsOrderId());

            phase.setPackageCode(PkgUtils.gen(apsOrder.getSeq()));
            phase.setStartDate(e.getPlanStartDateTime().toLocalDate());
            phase.setEndDate(e.getPlanEndDateTime().toLocalDate());
            phase.setDays((int) ChronoUnit.DAYS.between(e.getPlanStartDateTime().toLocalDate(), e.getPlanEndDateTime().toLocalDate()) + 1);
            phase.setTotalCount(apsOrder.getOrderQty());
//            phase.setPhaseName(phase1.getName());
//                    phase.setPhaseCode(e.getPhaseCode());
//                    phase.setWorkHours(new BigDecimal(e.getWorkTime()).divide(new BigDecimal(60 * 60), 2, RoundingMode.UP));
            return phase;
        }).collect(Collectors.toList());
        resVo.setPhaseData(collect1);


        // 查询当前订单的工作组负荷
        List<ApsDetailWeekday> groupAwdList = apsDetailWeekdayMapper.query()
                .select(" group_id, sum(work_time)  as work_time")
                .eq("order_id", reqVo.getId())
                .groupBy("group_id")
                .orderByDesc("work_time")
                .list();

//        List<Integer> groupCodeList = groupAwdList.stream().map(ApsDetailWeekday::getGroupId).collect(Collectors.toList());
//        List<Group> groupList = groupMapper.selectBatchIds(groupCodeList);
//        Map<Integer, Group> groupMap = groupList.stream().collect(Collectors.toMap(Group::getId, Function.identity()));
//        ReverseTop top = reverseTopMapper.lambdaQuery().eq(ReverseTop::getOrderId, order.getId()).one();
        List<ReverseOrderLoadsDayResVo> reverseOrderLoadsDayResVos = reverseOrderLoadsResVo.getTopTen();


        ReverseOrder ros = reverseOrderMapper.lambdaQuery().eq(ReverseOrder::getOrderId, order.getId()).isNull(ReverseOrder::getParentId).one();
        List<PreScheduledAnalysisReportGroupResVo> groupData = reverseOrderLoadsDayResVos.stream().map(e -> {
            PreScheduledAnalysisReportGroupResVo phase = new PreScheduledAnalysisReportGroupResVo();
            phase.setGroupName(e.getGroupName());
            phase.setCurrentWorkLoad(e.getWorkLoad().multiply(new BigDecimal(100)).divide(new BigDecimal(ros.getSplitNum()), 2, RoundingMode.UP));
            return phase;
        }).collect(Collectors.toList());
        resVo.setGroupData(groupData);
        // 查询生产的工作组负荷
//        Map<Integer, Integer> proWorkTime = proWorkTime(groupCodeList, resVo.getPlanStartDateTime().toLocalDate(), resVo.getPlanEndDateTime().toLocalDate());
//
//        // 计算工单总天数
////        Map<String, Integer> allWorkTime = new HashMap<>();
//        Map<Integer, Integer> allWorkTime = allWorkTime(groupCodeList, resVo.getPlanStartDateTime().toLocalDate(), resVo.getPlanEndDateTime().toLocalDate());
//
//
//        List<PreScheduledAnalysisReportGroupResVo> groupData = groupAwdList.stream()
//                .map(e -> {
//                    PreScheduledAnalysisReportGroupResVo phase = new PreScheduledAnalysisReportGroupResVo();
//                    Group group = groupMap.get(e.getGroupId());
//
//                    phase.setGroupName(group.getName());
//                    phase.setGroupCode(group.getMyCode());
//
//                    phase.setCurrentOrderSecond(e.getWorkTime());
//                    phase.setGroupOrderSecond(allWorkTime.get(e.getGroupId()));
//                    phase.setProOrderSecond(proWorkTime.get(e.getGroupId()));
//
//
//                    phase.setCurrentWorkLoad(new BigDecimal(phase.getCurrentOrderSecond()).divide(new BigDecimal(phase.getGroupOrderSecond()), 4, RoundingMode.UP));
//                    phase.setProWorkLoad(new BigDecimal(phase.getProOrderSecond()).divide(new BigDecimal(phase.getGroupOrderSecond()), 4, RoundingMode.UP));
//                    phase.setFreeWorkLoad(new BigDecimal(1).subtract(phase.getCurrentWorkLoad().add(phase.getProWorkLoad())));
//
//                    phase.setCurrentWorkLoad(phase.getCurrentWorkLoad().multiply(new BigDecimal(100)));
//                    phase.setProWorkLoad(phase.getProWorkLoad().multiply(new BigDecimal(100)));
//                    phase.setFreeWorkLoad(phase.getFreeWorkLoad().multiply(new BigDecimal(100)));
//                    return phase;
//                })
//                .sorted(Comparator.comparing(PreScheduledAnalysisReportGroupResVo::getFreeWorkLoad))
//                .limit(10)
//                .collect(Collectors.toList());
//        resVo.setGroupData(groupData);


        // ---- 原材料需求 -----
//        apsWorkDetailMapper.findYuancailiao();

        // 钢板数据
        List<OrderBom> workOrderList = apsWorkDetailMapper.findSplitBom(order.getId());
        Map<String, PreScheduledAnalysisReportPlateResVo> collect = workOrderList.stream()
                .map(e -> {
                    PreScheduledAnalysisReportPlateResVo r = new PreScheduledAnalysisReportPlateResVo();
                    r.setCailiao(e.getStandards());
                    r.setTotalCount(e.getTotalCount()
                            .multiply(e.getMaterWeight())
                            .multiply(new BigDecimal(order.getOrderQty()))
                            .divide(new BigDecimal("1000000").multiply(new BigDecimal("0.8")), 2, RoundingMode.UP));

                    r.setType(1);
                    return r;
                })
                .collect(Collectors.toMap(PreScheduledAnalysisReportPlateResVo::getCailiao, Function.identity(), (a, b) -> {
                    a.setTotalCount(a.getTotalCount().add(b.getTotalCount()));
                    return a;
                }));

        List<PreScheduledAnalysisReportPlateResVo> plateData = Lists.newArrayList(collect.values());
        plateData.sort(Comparator.comparing(PreScheduledAnalysisReportPlateResVo::getTotalCount));
        resVo.setPlateData(plateData);
        BigDecimal totalCount = collect.values().stream().map(PreScheduledAnalysisReportPlateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
        resVo.setPlateCount(totalCount);

//        List<ApsWorkOrder> wlAwdList = apsWorkOrderMapper.query()
//                .select("plm_id, sum(order_count) as total_count")
//                .eq("order_id", reqVo.getId())
//                .eq("work_chain", "WL")
//                .groupBy("plm_id")
//                .orderByDesc("total_count")
//                .list();

        List<OrderBom> bomList = orderBomMapper.lambdaQuery().eq(OrderBom::getOrderId, order.getId()).list();
        Map<String, OrderBom> collect2 = bomList.stream().collect(Collectors.toMap(OrderBom::getPlmId, Function.identity(), (e1, e2) -> e1));


        plateData.addAll(bomList.stream().filter(e -> e.getPlmId().startsWith("020")).map(e->{
            PreScheduledAnalysisReportPlateResVo r = new PreScheduledAnalysisReportPlateResVo();
            OrderBom orderBom = collect2.get(e.getPlmId());
            r.setCailiao(orderBom.getStandards());
            r.setTotalCount(e.getTotalCount().multiply(new BigDecimal(order.getOrderQty())));
            r.setType(2);
            return r;
        }).collect(Collectors.toMap(PreScheduledAnalysisReportPlateResVo::getCailiao, Function.identity(), (PreScheduledAnalysisReportPlateResVo a, PreScheduledAnalysisReportPlateResVo b) -> {
            a.setTotalCount(a.getTotalCount().add(b.getTotalCount()));
            return a;
        })).values());

        plateData.addAll(bomList.stream().filter(e -> e.getPlmId().startsWith("050")).map(e->{
            PreScheduledAnalysisReportPlateResVo r = new PreScheduledAnalysisReportPlateResVo();
            OrderBom orderBom = collect2.get(e.getPlmId());

            r.setCailiao(orderBom.getStandards());
            r.setTotalCount(e.getTotalCount().multiply(new BigDecimal(order.getOrderQty())));
            r.setType(3);
            return r;
        }).collect(Collectors.toMap(PreScheduledAnalysisReportPlateResVo::getCailiao, Function.identity(), (PreScheduledAnalysisReportPlateResVo a, PreScheduledAnalysisReportPlateResVo b) -> {
            a.setTotalCount(a.getTotalCount().add(b.getTotalCount()));
            return a;
        })).values());
        plateData.sort(Comparator.comparing(PreScheduledAnalysisReportPlateResVo::getTotalCount).reversed());

        // 焊丝
        BigDecimal hansiCount = plateData.stream().filter(e -> e.getType().equals(2)).map(PreScheduledAnalysisReportPlateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
        resVo.setWeldingWireCount(hansiCount);

        // 漆粉
        BigDecimal qifenCount = plateData.stream().filter(e -> e.getType().equals(3)).map(PreScheduledAnalysisReportPlateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
        resVo.setLacquerPowderCount(qifenCount);

//        List<SinoCamReceiveNestPlate> plateList = sinoCamReceiveNestPlateMapper.findPlateByOrder(order.getId());
//        if(!plateList.isEmpty()) {
//            List<Integer> collect = plateList.stream().map(SinoCamReceiveNestPlate::getReceiveNestId).distinct().collect(Collectors.toList());
//            List<SinoCamReceiveNestNest> planList = sinoCamReceiveNestNestMapper.selectBatchIds(collect);
//
//            Map<String, Long> collect1 = planList.stream().collect(Collectors.groupingBy(SinoCamReceiveNestNest::getNestDate, Collectors.counting()));
//            List<PreScheduledAnalysisReportPlateResVo> plateData = collect1.keySet().stream().sorted().map(e -> {
//                PreScheduledAnalysisReportPlateResVo plate = new PreScheduledAnalysisReportPlateResVo();
//                plate.setDay(LocalDate.parse(e, DatePattern.PURE_DATE_FORMATTER));
//                plate.setCount(collect1.get(e).intValue());
//                return plate;
//            }).collect(Collectors.toList());
//
//            resVo.setPlateData(plateData);
//
//        }
//        resVo.setPlateCount(plateList.size());

        // ---- 外购件 -----

        // 排除原材料
//        wlAwdList = wlAwdList.stream().filter(e -> !(e.getPlmId().startsWith("010") || e.getPlmId().startsWith("020") || e.getPlmId().startsWith("050"))).collect(Collectors.toList());


//        List<OrderBom> list = orderBomMapper.lambdaQuery()
//                .in(OrderBom::getPlmId, wlAwdList.stream().map(ApsWorkOrder::getPlmId).collect(Collectors.toList()))
//                .eq(OrderBom::getOrderId, order.getId())
//                .list();

//        Map<String, OrderBom> map = list.stream().collect(Collectors.toMap(OrderBom::getPlmId, Function.identity(), (k1, k2) -> k1));

        List<PreScheduledAnalysisReportWlResVo> wlList = bomList.stream()
                .filter(e -> e.getWorkChain().equals("WL"))
//                .filter(e -> !e.getErpCode().startsWith("010") && !e.getErpCode().startsWith("020") && !e.getErpCode().startsWith("050"))
                .map(e -> {
                    PreScheduledAnalysisReportWlResVo wl = new PreScheduledAnalysisReportWlResVo();
//                    OrderBom orderBom = map.get(e.getPlmId());
                    wl.setPlmId(e.getPlmId());
                    wl.setMaterName(e.getMaterName());
                    wl.setTotalCount(e.getTotalCount());
                    return wl;
                })
                .sorted(Comparator.comparing(PreScheduledAnalysisReportWlResVo::getTotalCount).reversed())
                .collect(Collectors.toList());

        resVo.setWlData(wlList);

        BigDecimal bigDecimal = wlList.stream().map(PreScheduledAnalysisReportWlResVo::getTotalCount).reduce(BigDecimal::add).get();
        resVo.setWlCount(bigDecimal);



//        主要产品部件：顶梁；订单数量：50；计划时间：2024-05-01~2024-05-30。
//        该订单生产平衡率：50.55%；建议通过调整加工工时，或者替换工作中心，对工艺进行优化，满足生产平衡率基线（平衡率<80%）满足当前工艺要求可以按照当前工艺进行排产(平衡率>=80%)。
//        该订单当前瓶颈工序为：坡口，板加，立焊，平焊，喷涂；建议工艺人员可以重新分配资源，比如，工序委外，部分任务转移到其他负荷较轻的工作组。
//        给出订单拆分建议：11批次；每次处理较小批次的产品，可以更好地匹配生产线的产能和负荷，避免资源浪费或产能过剩。
        return resVo;
    }

//
//    public Map<Integer, Integer> proWorkTime(List<Integer> groupCodeList, LocalDate startDate, LocalDate endDate) {
//
//        List<ApsDetailWeekday> groupAwdList = apsDetailWeekdayMapper.query()
//                .select(" group_id, sum(work_time) as work_time ")
//                .eq("order_type", 1)
//                .in("group_id", groupCodeList)
//                .ge("shift_day", startDate)
//                .le("shift_day",  endDate)
//                .groupBy("group_id")
//                .orderByDesc("work_time")
//                .list();
//
//        Map<Integer, Integer> collect = groupAwdList.stream().collect(Collectors.toMap(ApsDetailWeekday::getGroupId, ApsDetailWeekday::getWorkTime));
//        Map<Integer, Integer> map = new HashMap<>();
//
//        for (Integer groupId : groupCodeList) {
//            map.put(groupId, collect.getOrDefault(groupId, 0));
//
//        }
//
//        return map;
//    }
//
//
//    public Map<Integer, Integer> allWorkTime(List<Integer> groupCodeList, LocalDate startDate, LocalDate endDate) {
//        List<StationGroup> stationGroupList = stationGroupMapper.lambdaQuery().in(StationGroup::getGroupId, groupCodeList).list();
//        List<Integer> stationIdList = stationGroupList.stream().map(StationGroup::getStationId).distinct().collect(Collectors.toList());
//        List<StationWeekdayScheme> schemeList = stationWeekdaySchemeMapper.lambdaQuery().in(StationWeekdayScheme::getStationId, stationIdList).list();
//        List<Shift> shifts = shiftMapper.lambdaQuery().in(Shift::getShiftTypeId, schemeList.stream().map(StationWeekdayScheme::getShiftTypeId).distinct().collect(Collectors.toList())).list();
//        List<StationWeekdayRule> ruleList = stationWeekdayRuleMapper.lambdaQuery().in(StationWeekdayRule::getStationWeekdaySchemeId, schemeList.stream().map(BaseEntity::getId).collect(Collectors.toList())).list();
//        List<BreakPeriod> bp = breakPeriodMapper.lambdaQuery().in(BreakPeriod::getShiftId, shifts.stream().map(BaseEntity::getId).collect(Collectors.toList())).list();
//
//        List<LocalDate> days = Stream.iterate(startDate, s -> s.plusDays(1)).limit(startDate.until(endDate, ChronoUnit.DAYS) + 1).collect(Collectors.toList());
//
//        // 构建缓存
//        Map<Integer, List<StationGroup>> groupMap = stationGroupList.stream().collect(Collectors.groupingBy(StationGroup::getGroupId));
//        Map<Integer, StationWeekdayScheme> schemeMap = schemeList.stream().collect(Collectors.toMap(StationWeekdayScheme::getStationId, Function.identity()));
//        Map<Integer, List<Shift>> shiftMap = shifts.stream().collect(Collectors.groupingBy(Shift::getShiftTypeId));
//        Map<Integer, List<StationWeekdayRule>> ruleMap = ruleList.stream().collect(Collectors.groupingBy(StationWeekdayRule::getStationWeekdaySchemeId));
//        Map<Integer, List<BreakPeriod>> bpMap = bp.stream().collect(Collectors.groupingBy(BreakPeriod::getShiftId));
//
//        Map<Integer, Integer> map = new HashMap<>();
//        for (Integer groupId : groupCodeList) {
//
//            int sum = 0;
//            List<StationGroup> stationGroups = groupMap.get(groupId);
//            if(stationGroups == null || stationGroups.isEmpty()){
//                map.put(groupId, sum);
//                continue;
//            }
//
//            for (StationGroup stationGroup : stationGroups) {
//                for (LocalDate day : days) {
//                    Integer stationSecond = getStationSecondCache(stationGroup.getStationId(), day, shiftMap, ruleMap, bpMap);
//                    sum += stationSecond;
//                }
//            }
//
//            map.put(groupId, sum);
//
//        }
//
//        return map;
//    }

    public Integer getStationSecondCache(Station station, LocalDate day,
                                          Map<Integer, List<Shift>> shiftMap,
                                          Map<Integer, List<WeekdayRule>> ruleMap,
                                          Map<Integer, List<BreakPeriod>> bpMap) {
//        StationWeekdayScheme scheme = schemeMap.get(stationId);
//        if(scheme == null){
//            throw SyExceptionUtils.e("没有找到该工位的日历");
//        }
        List<Shift> shifts = shiftMap.get(station.getShiftTypeId());
        List<WeekdayRule> ruleList = ruleMap.get(station.getShiftTypeId());

        List<ApsWeekDay> allWeekday = shifts.stream()
                .map(shift -> {
                    ApsWeekDay newDay = new ApsWeekDay();
                    MkBeanUtils.copyPropertiesIgnore(shift, newDay);
                    newDay.setSchemeId(station.getShiftTypeId());
                    newDay.setShiftDay(day);
                    newDay.setShiftSeq(shift.getSeq());
                    newDay.setShiftId(shift.getId());
                    newDay.setStartDateTime(LocalDateTime.of(day.plusDays(shift.getOffsetStartDay()), shift.getStartTime()));
                    newDay.setEndDateTime(LocalDateTime.of(day.plusDays(shift.getOffsetEndDay()), shift.getEndTime()));
                    return newDay;
                })
                .flatMap(newDay -> {

                    List<ApsWeekDay> all = new ArrayList<>();
                    List<BreakPeriod> bp = bpMap.getOrDefault(newDay.getShiftId(), new ArrayList<>());
                    for (BreakPeriod breakPeriod : bp) {
                        if(LocalDateTime.of(day.plusDays(breakPeriod.getOffsetStartDay()), breakPeriod.getStartTime()).isAfter(newDay.getEndDateTime())){
                            continue;
                        }
                        if(LocalDateTime.of(day.plusDays(breakPeriod.getOffsetEndDay()), breakPeriod.getEndTime()).isBefore(newDay.getStartDateTime())){
                            continue;
                        }

                        ApsWeekDay apsWeekDay = MkBeanUtils.copyProperties(newDay, ApsWeekDay.class);
                        apsWeekDay.setEndDateTime(LocalDateTime.of(day.plusDays(breakPeriod.getOffsetStartDay()), breakPeriod.getStartTime()));
                        all.add(apsWeekDay);
                        newDay.setStartDateTime(LocalDateTime.of(day.plusDays(breakPeriod.getOffsetEndDay()), breakPeriod.getEndTime()));
                    }
                    all.add(newDay);
                    return all.stream();
                }).collect(Collectors.toList());



        // 匹配规则，可以抽成策略模式，数据量大，为了效率不建议
        for (ApsWeekDay w : allWeekday) {
            for (WeekdayRule weekdayRule : ruleList) {
                if (weekdayRule.getType() == 4) {
                    if (weekdayRule.getValue().equals(String.valueOf(w.getShiftDay().getDayOfWeek().getValue()))) {
                        w.setDateType(weekdayRule.getDateType());
                        w.setWorkState(weekdayRule.getStatus());
                        w.setWeekdayRuleId(weekdayRule.getId());
                    }
                } else if (weekdayRule.getType() == 1){
                    String value = weekdayRule.getValue();
                    String[] split = value.split(ProjectConstants.WEEKDAY_RULE_WEEKVALUE_SPLIT);
                    LocalDate start = LocalDate.parse(split[0], DatePattern.NORM_DATE_FORMATTER);
                    LocalDate end = LocalDate.parse(split[1], DatePattern.NORM_DATE_FORMATTER);
                    if (!w.getShiftDay().isBefore(start) && !w.getShiftDay().isAfter(end)) {
                        w.setDateType(weekdayRule.getDateType());
                        w.setWorkState(weekdayRule.getStatus());
                        w.setText(weekdayRule.getText());
                        w.setWeekdayRuleId(weekdayRule.getId());
                    }

                } else if (weekdayRule.getType() == 3) {
                    if (LocalDate.parse(weekdayRule.getValue(), DatePattern.NORM_DATE_FORMATTER).equals(w.getShiftDay())) {
                        w.setDateType(weekdayRule.getDateType());
                        w.setWorkState(weekdayRule.getStatus());
                        w.setText(weekdayRule.getText());
                        w.setWeekdayRuleId(weekdayRule.getId());
                    }
                } else if (weekdayRule.getType() == 2) {
                    String value = weekdayRule.getValue();
                    String[] split = value.split(ProjectConstants.WEEKDAY_RULE_WEEKVALUE_SPLIT);
                    if (split[0].equals(w.getShiftDay().toString()) && split[1].equals(String.valueOf(w.getShiftId()))) {
                        w.setDateType(weekdayRule.getDateType());
                        w.setWorkState(weekdayRule.getStatus());
                        w.setText(weekdayRule.getText());
                        w.setWeekdayRuleId(weekdayRule.getId());
                    }
                }
            }
        }

        Long reduce1 = allWeekday.stream().filter(w -> w.getWorkState() == 1).map(w -> w.getStartDateTime().until(w.getEndDateTime(), ChronoUnit.SECONDS)).reduce(0L, Long::sum);
        return reduce1.intValue();
    }



    public List<PreScheduledAnalysisPhaseResVo> analysisPhase(PreScheduledAnalysisPhaseReqVo reqVo) {
        List<ApsDetailWeekday> list = apsDetailWeekdayMapper.query()
                .select("shift_day, phase_code,sum(work_time) work_time")
                .eq("order_id", reqVo.getId())
                .ge(reqVo.getStartDate() != null, "shift_day", reqVo.getStartDate())
                .le(reqVo.getEndDate() != null, "shift_day", reqVo.getEndDate())
                .groupBy("shift_day, phase_code")
                .orderByAsc("shift_day")
                .orderByDesc("work_time")
                .list();

        Map<LocalDate, List<ApsDetailWeekday>> collect = list.stream().collect(Collectors.groupingBy(ApsDetailWeekday::getShiftDay, Collectors.toList()));

        Map<String, Phase> phaseMap = phaseMapper.lambdaQuery().isNotNull(Phase::getMyCode).list().stream().collect(Collectors.toMap(Phase::getMyCode, Function.identity()));


        return collect.keySet().stream().sorted().map(e -> {
                    PreScheduledAnalysisPhaseResVo preScheduledAnalysisPhaseResVo = new PreScheduledAnalysisPhaseResVo();
                    preScheduledAnalysisPhaseResVo.setDay(e);
                    List<ApsDetailWeekday> apsDetailWeekdays = collect.get(e);
                    if (apsDetailWeekdays != null) {
                        List<PreScheduledAnalysisPhaseDayResVo> collect2 = apsDetailWeekdays.stream().map(e1 -> {
                            PreScheduledAnalysisPhaseDayResVo preScheduledAnalysisPhaseItemResVo = new PreScheduledAnalysisPhaseDayResVo();
                            preScheduledAnalysisPhaseItemResVo.setPhaseCode(e1.getPhaseCode());
                            preScheduledAnalysisPhaseItemResVo.setPhaseName(phaseMap.get(e1.getPhaseCode()).getName());
                            preScheduledAnalysisPhaseItemResVo.setWorkHour(new BigDecimal(e1.getWorkTime()).divide(new BigDecimal(60 * 60), 2, RoundingMode.UP));
                            return preScheduledAnalysisPhaseItemResVo;
                        }).collect(Collectors.toList());
                        preScheduledAnalysisPhaseResVo.setData(collect2);
                    }

                    return preScheduledAnalysisPhaseResVo;
                })
                .collect(Collectors.toList());
    }

    public List<PreScheduledAnalysisGroupResVo> analysisGroup(PreScheduledAnalysisGroupReqVo reqVo) {
        Order order = orderMapper.selectById(reqVo.getId());
        if(reqVo.getStartDate() == null){
            reqVo.setStartDate(order.getPlanStartDateTime().toLocalDate());
        }

        if(reqVo.getEndDate() == null){
            reqVo.setEndDate(order.getPlanEndDateTime().toLocalDate());
        }

        // 查询当前订单的工作组负荷
        List<ApsDetailWeekday> groupAwdList = apsDetailWeekdayMapper.query()
                .select(" group_id, shift_day, sum(work_time)  as work_time")
                .eq("order_id", reqVo.getId())
                .groupBy("group_id, shift_day")
                .orderByDesc("work_time")
                .list();

        List<Integer> groupIdList = groupAwdList.stream().map(ApsDetailWeekday::getGroupId).distinct().collect(Collectors.toList());
        List<Group> groupList = groupMapper.selectBatchIds(groupIdList);

        // 查询生产的工作组负荷
        List<ApsDetailWeekday> groupAwdListPro = apsDetailWeekdayMapper.query()
                .select(" group_id, shift_day, sum(work_time) as work_time ")
                .eq("order_type", 1)
                .in("group_id", groupIdList)
                .ge("shift_day", reqVo.getStartDate())
                .le("shift_day",  reqVo.getEndDate())
                .groupBy("group_id, shift_day")
                .orderByDesc("work_time")
                .list();

        // 计算工作组的总负荷
//        List<StationGroup> stationGroupList = stationGroupMapper.lambdaQuery().in(StationGroup::getGroupId, groupIdList).list();
//        List<Integer> stationIdList = stationGroupList.stream().map(StationGroup::getStationId).distinct().collect(Collectors.toList());
//        List<StationWeekdayScheme> schemeList = stationWeekdaySchemeMapper.lambdaQuery().in(StationWeekdayScheme::getStationId, stationIdList).list();
//        List<Station> stationList = stationMapper.selectBatchIds(stationIdList);
        List<Station> stationList = stationMapper.lambdaQuery().in(Station::getGroupId, groupIdList).list();
        List<Integer> shiftTypeIds = stationList.stream().map(Station::getShiftTypeId).distinct().collect(Collectors.toList());

        List<Shift> shifts = shiftMapper.lambdaQuery().in(Shift::getShiftTypeId, shiftTypeIds).list();
        List<WeekdayRule> ruleList = weekdayRuleMapper.lambdaQuery().in(WeekdayRule::getShiftTypeId, shiftTypeIds).list();
        List<BreakPeriod> bp = breakPeriodMapper.lambdaQuery().in(BreakPeriod::getShiftId, shifts.stream().map(BaseEntity::getId).collect(Collectors.toList())).list();


        // 构建缓存
//        Map<Integer, List<StationGroup>> groupMap = stationGroupList.stream().collect(Collectors.groupingBy(StationGroup::getGroupId));
        Map<Integer, List<Station>> groupMap = stationList.stream().collect(Collectors.groupingBy(Station::getGroupId));
//        Map<Integer, StationWeekdayScheme> schemeMap = schemeList.stream().collect(Collectors.toMap(StationWeekdayScheme::getStationId, Function.identity()));
        Map<Integer, List<Shift>> shiftMap = shifts.stream().collect(Collectors.groupingBy(Shift::getShiftTypeId));
        Map<Integer, List<WeekdayRule>> ruleMap = ruleList.stream().collect(Collectors.groupingBy(WeekdayRule::getShiftTypeId));
        Map<Integer, List<BreakPeriod>> bpMap = bp.stream().collect(Collectors.groupingBy(BreakPeriod::getShiftId));
        Map<Integer, Station> stationMap = stationList.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));

        List<LocalDate> days = Stream.iterate(reqVo.getStartDate(), s -> s.plusDays(1)).limit(reqVo.getStartDate().until(reqVo.getEndDate(), ChronoUnit.DAYS) + 1).collect(Collectors.toList());
        Map<Integer, Map<LocalDate, Integer>> map = new HashMap<>();

        for (Integer groupId : groupIdList) {

            List<Station> stationGroups = groupMap.get(groupId);
            if(stationGroups == null || stationGroups.isEmpty()){
                continue;
            }
            for (LocalDate day : days) {
                int sum = 0;
                for (Station stationGroup : stationGroups) {
                    Integer stationSecond = getStationSecondCache(stationMap.get(stationGroup.getId()), day, shiftMap, ruleMap, bpMap);
                    sum += stationSecond;
                }
                Map<LocalDate, Integer> localDateIntegerMap = map.computeIfAbsent(groupId, k -> new HashMap<>());
                localDateIntegerMap.put(day, sum);
            }

        }

        // 统计
        Map<LocalDate, Map<Integer, Integer>> orderDayGroupMap = groupAwdList.stream().collect(Collectors.groupingBy(ApsDetailWeekday::getShiftDay, Collectors.toMap(ApsDetailWeekday::getGroupId, ApsDetailWeekday::getWorkTime)));
        Map<LocalDate, Map<Integer, Integer>> proDayGroupMap = groupAwdListPro.stream().collect(Collectors.groupingBy(ApsDetailWeekday::getShiftDay, Collectors.toMap(ApsDetailWeekday::getGroupId, ApsDetailWeekday::getWorkTime)));

        Map<Integer, Group> groupIdMap = groupList.stream().collect(Collectors.toMap(BaseEntity::getId, e -> e));

        List<PreScheduledAnalysisGroupResVo> resVo = new ArrayList<>();
        for (Integer groupId : groupIdList) {
            PreScheduledAnalysisGroupResVo groupData = new PreScheduledAnalysisGroupResVo();
            Group group = groupIdMap.get(groupId);
            groupData.setGroupCode(group.getMyCode());
            groupData.setGroupName(group.getName());
            resVo.add(groupData);

            for (LocalDate day : days) {
                PreScheduledAnalysisGroupGroupResVo dayData = new PreScheduledAnalysisGroupGroupResVo();
                dayData.setDay(day);

                dayData.setGroupName(group.getName());
                dayData.setGroupCode(group.getMyCode());

                dayData.setCurrentOrderSecond(orderDayGroupMap.getOrDefault(day, new HashMap<>()).getOrDefault(group.getId(), 0));
                dayData.setProOrderSecond(proDayGroupMap.getOrDefault(day, new HashMap<>()).getOrDefault(group.getId(), 0));
                dayData.setGroupOrderSecond(map.getOrDefault(group.getId(), new HashMap<>()).getOrDefault(day, 0));
                if(dayData.getGroupOrderSecond() != 0){

                    Integer currentOrderSecond = dayData.getCurrentOrderSecond();
                    Integer groupOrderSecond = dayData.getGroupOrderSecond();
                    Integer proOrderSecond = dayData.getProOrderSecond();

                    if(currentOrderSecond + proOrderSecond > groupOrderSecond){
                        groupOrderSecond = currentOrderSecond + proOrderSecond;
                    }

                    dayData.setCurrentWorkLoad(new BigDecimal(currentOrderSecond).divide(new BigDecimal(groupOrderSecond), 4, RoundingMode.HALF_UP));
                    dayData.setProWorkLoad(new BigDecimal(proOrderSecond).divide(new BigDecimal(groupOrderSecond), 4, RoundingMode.HALF_UP));
                    dayData.setFreeWorkLoad(new BigDecimal(1).subtract(dayData.getCurrentWorkLoad().add(dayData.getProWorkLoad())));

                    dayData.setCurrentWorkLoad(dayData.getCurrentWorkLoad().multiply(new BigDecimal(100)));
                    dayData.setProWorkLoad(dayData.getProWorkLoad().multiply(new BigDecimal(100)));
                    dayData.setFreeWorkLoad(dayData.getFreeWorkLoad().multiply(new BigDecimal(100)));
                } else {
                    dayData.setCurrentWorkLoad(new BigDecimal(0));
                    dayData.setProWorkLoad(new BigDecimal(0));
                    dayData.setFreeWorkLoad(new BigDecimal(0));
                }

                groupData.getGroupData().add(dayData);
            }
        }

        resVo.sort(Comparator.comparing(PreScheduledAnalysisGroupResVo::getGroupCode));
        return resVo;



//        return days.stream()
//                .map(day->{
//                    PreScheduledAnalysisGroupResVo resVo = new PreScheduledAnalysisGroupResVo();
//                    resVo.setDay(day);
//
//                    List<PreScheduledAnalysisGroupDayResVo> collect = groupList.stream().map(group -> {
//
//
//                        PreScheduledAnalysisGroupDayResVo rr = new PreScheduledAnalysisGroupDayResVo();
//                        rr.setGroupName(group.getName());
//                        rr.setGroupCode(group.getMyCode());
//
//                        rr.setCurrentOrderSecond(orderDayGroupMap.getOrDefault(day, new HashMap<>()).getOrDefault(group.getId(), 0));
//                        rr.setProOrderSecond(proDayGroupMap.getOrDefault(day, new HashMap<>()).getOrDefault(group.getId(), 0));
//                        rr.setGroupOrderSecond(map.getOrDefault(group.getId(), new HashMap<>()).getOrDefault(day, 0));
//                        if(rr.getGroupOrderSecond() != 0){
//                            rr.setCurrentWorkLoad(new BigDecimal(rr.getCurrentOrderSecond()).divide(new BigDecimal(rr.getGroupOrderSecond()), 4, RoundingMode.UP));
//                            rr.setProWorkLoad(new BigDecimal(rr.getProOrderSecond()).divide(new BigDecimal(rr.getGroupOrderSecond()), 4, RoundingMode.UP));
//                            rr.setFreeWorkLoad(new BigDecimal(1).subtract(rr.getCurrentWorkLoad().add(rr.getProWorkLoad())));
//
//                            rr.setCurrentWorkLoad(rr.getCurrentWorkLoad().multiply(new BigDecimal(100)));
//                            rr.setProWorkLoad(rr.getProWorkLoad().multiply(new BigDecimal(100)));
//                            rr.setFreeWorkLoad(rr.getFreeWorkLoad().multiply(new BigDecimal(100)));
//                        } else {
//                            rr.setCurrentWorkLoad(new BigDecimal(0));
//                            rr.setProWorkLoad(new BigDecimal(0));
//                            rr.setFreeWorkLoad(new BigDecimal(0));
//                        }
//
//
//
//                        return rr;
//                    })
//                    .filter(rr->rr.getCurrentOrderSecond() != 0)
//                    .collect(Collectors.toList());
//                    resVo.setData(collect);
//                    return resVo;
//                })
//                .collect(Collectors.toList());

//        Map<Integer, Integer> allWorkTime = allWorkTime(groupIdList, reqVo.getStartDate(), reqVo.getEndDate());
//
//
//        List<PreScheduledAnalysisReportGroupResVo> groupData = groupAwdList.stream()
//                .map(e -> {
//                    PreScheduledAnalysisReportGroupResVo phase = new PreScheduledAnalysisReportGroupResVo();
//                    Group group = groupMap.get(e.getGroupId());
//
//                    phase.setGroupName(group.getName());
//                    phase.setGroupCode(group.getMyCode());
//
//                    phase.setCurrentOrderSecond(e.getWorkTime());
//                    phase.setGroupOrderSecond(allWorkTime.get(e.getGroupId()));
//                    phase.setProOrderSecond(proWorkTime.get(e.getGroupId()));
//
//
//                    phase.setCurrentWorkLoad(new BigDecimal(phase.getCurrentOrderSecond()).divide(new BigDecimal(phase.getGroupOrderSecond()), 4, RoundingMode.UP));
//                    phase.setProWorkLoad(new BigDecimal(phase.getProOrderSecond()).divide(new BigDecimal(phase.getGroupOrderSecond()), 4, RoundingMode.UP));
//                    phase.setFreeWorkLoad(new BigDecimal(1).subtract(phase.getCurrentWorkLoad().add(phase.getProWorkLoad())));
//
//                    phase.setCurrentWorkLoad(phase.getCurrentWorkLoad().multiply(new BigDecimal(100)));
//                    phase.setProWorkLoad(phase.getProWorkLoad().multiply(new BigDecimal(100)));
//                    phase.setFreeWorkLoad(phase.getFreeWorkLoad().multiply(new BigDecimal(100)));
//                    return phase;
//                })
//                .sorted(Comparator.comparing(PreScheduledAnalysisReportGroupResVo::getFreeWorkLoad))
//                .limit(10)
//                .collect(Collectors.toList());
//        resVo.setGroupData(groupData);


        /*
        List<ApsDetailWeekday> list = apsDetailWeekdayMapper.query()
                .select("shift_day, group_id,sum(work_time) work_time")
                .eq("order_id", reqVo.getId())
                .ge(reqVo.getStartDate() != null, "shift_day", reqVo.getStartDate())
                .le(reqVo.getEndDate() != null, "shift_day", reqVo.getEndDate())
                .groupBy("shift_day, group_id")
                .orderByAsc("shift_day")
                .orderByDesc("work_time")
                .list();

        Map<LocalDate, List<ApsDetailWeekday>> collect = list.stream().collect(Collectors.groupingBy(ApsDetailWeekday::getShiftDay, Collectors.toList()));


        List<Group> groupList = groupMapper.lambdaQuery().isNotNull(Group::getMyCode).list();
        Map<Integer, Group> groupMap = groupList.stream().collect(Collectors.toMap(Group::getId, Function.identity()));
        long until = reqVo.getStartDate().until(reqVo.getStartDate(), ChronoUnit.DAYS) + 1;


        return collect.keySet().stream().sorted().map(e -> {
                    PreScheduledAnalysisGroupResVo preScheduledAnalysisGroupResVo = new PreScheduledAnalysisGroupResVo();
                    preScheduledAnalysisGroupResVo.setDay(e);
                    List<ApsDetailWeekday> apsDetailWeekdays = collect.get(e);
                    if (apsDetailWeekdays != null) {
                        List<PreScheduledAnalysisGroupDayResVo> collect2 = apsDetailWeekdays.stream().map(e1 -> {
                            PreScheduledAnalysisGroupDayResVo preScheduledAnalysisPhaseItemResVo = new PreScheduledAnalysisGroupDayResVo();
                            preScheduledAnalysisPhaseItemResVo.setGroupCode(groupMap.get(e1.getGroupId()).getMyCode());
                            preScheduledAnalysisPhaseItemResVo.setGroupName(groupMap.get(e1.getGroupId()).getName());
                            preScheduledAnalysisPhaseItemResVo.setWorkLoad(new BigDecimal(e1.getWorkTime()).divide(new BigDecimal(until), 2, RoundingMode.UP).divide(new BigDecimal(projectConfig.getDaySecond()), 2, RoundingMode.UP));
                            return preScheduledAnalysisPhaseItemResVo;
                        }).collect(Collectors.toList());
                        preScheduledAnalysisGroupResVo.setData(collect2);
                    }

                    return preScheduledAnalysisGroupResVo;
                })
                .collect(Collectors.toList());

         */
    }

    public List<PreScheduledAnalysisWlResVo> analysisWl(PreScheduledAnalysisWlReqVo reqVo) {
        Order order = orderMapper.selectById(reqVo.getId());
        List<ApsWorkOrder> wlAwdList = apsWorkOrderMapper.query()
                .select("DATE_FORMAT(plan_start_date_time,'%Y-%m-%d') due_date, plm_id, sum(order_count) as total_count")
                .eq("order_id", reqVo.getId())
                .eq("work_chain", "WL")
                .groupBy("DATE_FORMAT(plan_start_date_time,'%Y-%m-%d'), plm_id")
                .list();

//        wlAwdList = wlAwdList.stream().filter(e -> !(e.getErpCode().startsWith("010") || e.getErpCode().startsWith("020") || e.getErpCode().startsWith("050"))).collect(Collectors.toList());

        List<OrderBom> list = orderBomMapper.lambdaQuery()
                .in(OrderBom::getPlmId, wlAwdList.stream().map(ApsWorkOrder::getPlmId).collect(Collectors.toList()))
                .eq(OrderBom::getOrderId, order.getId())
                .list();

        Map<String, OrderBom> map = list.stream().collect(Collectors.toMap(OrderBom::getPlmId, Function.identity(), (k1, k2) -> k1));

        Map<String, List<ApsWorkOrder>> collect = wlAwdList.stream().collect(Collectors.groupingBy(ApsWorkOrder::getDueDate, Collectors.toList()));



        return collect.keySet().stream().sorted().map(e -> {
                    PreScheduledAnalysisWlResVo preScheduledAnalysisGroupResVo = new PreScheduledAnalysisWlResVo();
                    preScheduledAnalysisGroupResVo.setDay(LocalDate.parse(e, DatePattern.NORM_DATE_FORMATTER));
                    List<ApsWorkOrder> apsDetailWeekdays = collect.get(e);
                    if (apsDetailWeekdays != null) {
                        List<PreScheduledAnalysisGroupWlResVo> collect2 = apsDetailWeekdays.stream().map(e1 -> {
                            PreScheduledAnalysisGroupWlResVo preScheduledAnalysisPhaseItemResVo = new PreScheduledAnalysisGroupWlResVo();
                            OrderBom orderBom = map.get(e1.getPlmId());
                            preScheduledAnalysisPhaseItemResVo.setPlmId(orderBom.getPlmId());
                            preScheduledAnalysisPhaseItemResVo.setMaterName(orderBom.getMaterName());
                            preScheduledAnalysisPhaseItemResVo.setTotalCount(e1.getTotalCount());
                            return preScheduledAnalysisPhaseItemResVo;
                        }).collect(Collectors.toList());
                        preScheduledAnalysisGroupResVo.setData(collect2);
                        collect2.sort((o1, o2) -> o2.getTotalCount().compareTo(o1.getTotalCount()));
                    }

                    return preScheduledAnalysisGroupResVo;
                })
               .collect(Collectors.toList());
    }
}
